<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>简易串口App</title>
    <style>
        html,body{height:100%}

        body{ margin:0;
            overflow: hidden;
        }

        .container {
            display: flex;
            flex-direction: column;
            height: 100%;
        }

        #connect_box {
            padding: 4px 4px;
            height: 40px;
            display: flex;
            justify-content: space-around; 
            background: #dddddd;
        }

        #baudrate_list {
            margin: 0px 4px;
            flex-grow: 5;
        }

        #request_button {
            flex-grow: 1;
            width: 70px;
        }

        #open_button {
            flex-grow: 1;
            width: 70px;
        }

        #clean_button {
            margin-left: 4px;
            flex-grow: 1;
            width: 70px;
        }

        #buffer {
            overflow:auto;
            height: 100%;
            padding: 10px;
        }

        #send_box {
            padding: 5px;
            height: 40px;
            background: #dddddd;
            display: flex;
        }

        #send_content {
            flex: 1;
        }

        #send_button {
            width: 70px;
            margin-left: 4px;
        }

        .autoscroll_box {
            margin-right: 10px;
            display: flex;
            flex-direction: row;
            align-items: center;
        }

        .alert {
            display: none;
            position: absolute;
            background: #00cc00;
            border: solid 1px #999999;
            border-radius: 4px;
            text-align: center;
            color: #fff;
            padding: 8px;
        }

    </style>
</head>
<body>
    <div class="container">
    <!-- Box for choosing the port to connect to -->
        <div id="connect_box">
            <button id="request_button">选择端口</button>
            <select id="baudrate_list">
                <option value="110">110波特率</option>
                <option value="300">300波特率</option>
                <option value="1200">1200波特率</option>
                <option value="2400">2400波特率</optionva>
                <option value="4800">4800波特率</option>
                <option value="9600" selected>9600波特率</option>
                <option value="14400">14400波特率</optionv>
                <option value="19200">19200波特率</option>
                <option value="38400">38400波特率</option>
                <option value="57600">57600波特率</option>
                <option value="115200">115200波特率</option>
            </select>
            <button id="open_button">打开串口</button>
            <button id="clean_button">清空输出</button>
        </div>

        <!-- Box for log messages -->
        <div id="buffer">
        </div>
        <div id="send_box">
            <div class="autoscroll_box">
                <input id="autoscroll_content" type="checkbox" checked>
                <span style="font-size: 12px;color: #333333">自动滚动</span>
            </div>
            <input id="send_content">
            <button id="send_button">发&nbsp;&nbsp;送</button>
        </div>
    </div>
    <div class="alert" id="alert"></div>
    <!-- Include main code -->
    <script>
        // ----------------------------------------
        // UI
        // ----------------------------------------
        // Vars
        const requestButton = document.getElementById("request_button");
        const openButton = document.getElementById("open_button");
        const cleanButton = document.getElementById("clean_button");
        const autoScrollCheckBox = document.getElementById("autoscroll_content");
        const sendButton = document.getElementById("send_button");
        const contentInput = document.getElementById("send_content");
        const baudRateOptions = document.getElementById("baudrate_list");

        var autoScroll = true;

        // Event
        requestButton.addEventListener('click', () => {
            requestPort();
        });

        // 打开串口按钮点击事件
        openButton.addEventListener('click', () => {
            if (connectionId > 0) {
                close();
            }
            else {
                open();
            }
        });

        // 清空输出
        cleanButton.addEventListener('click', () => {
            var buffer = document.getElementById('buffer');
            buffer.innerHTML = '';
        });

        // 自动滚动
        autoScrollCheckBox.addEventListener('click', function() {
            autoScroll = this.checked;
        });

        // 发送按钮
        sendButton.addEventListener('click', async function () {
            var msg = document.querySelector('#send_content').value;
            if (msg) {
                writeToPort(msg);
            }
            else {
                console.log('请输入数据');
                alertError('请输入数据', 5)
            }
        });


        // ----------------------------------------
        // Serial Port Opertions
        // ----------------------------------------
        var connectionId = -1;
        let port;
        let reader;

        // 监听设备插、拔状态
        document.addEventListener('DOMContentLoaded', async () => {
            console.log('DOMContentLoaded');
            // 监听连接状态
            navigator.serial.addEventListener('connect', (event) => {
                console.log('connected...');
            });
            navigator.serial.addEventListener('disconnect', (event) => {
                console.log('disconnected...');
            });
        });

        async function requestPort() {
            try {
                port = await navigator.serial.requestPort();
            } catch (e) {
                console.log(e);
            }
        }

        async function open() {
            openButton.innerHTML = '关闭串口';

            baudRate = baudRateOptions[baudRateOptions.selectedIndex].value;

            var conOp = {baudRate : Number(baudRate)}

            await port.open(conOp)
            if (port && port.readable) {
                connectionId = 1;
                reader = port.readable.getReader();
                readFromPort();
            }
        }

        async function close() {
            try {
                if (reader) {
                    await reader.cancel();
                }
                await port.close();
                alertSuccess('成功关闭串口！', 3);
                connectionId = -1;
                openButton.innerHTML = '打开串口';
            }
            catch (e) {
                console.log(e);
                alertError('关闭串口失败！', 5);
            }
        }

        async function readFromPort() {
            try {
                decoder = new TextDecoder();
                for (;;) {
                const {value, done} = await reader.read();
                if (value) {
                    writeToBuffer(decoder.decode(value));
                }
                if (done) {
                    break;
                }
                }
                reader.releaseLock();
                reader = undefined;
            }
            catch (e) {
                console.error(e);
            }
        }

        async function writeToPort(msg) {
            if (port.writable && msg) {
                const encoder = new TextEncoder();
                const writer = port.writable.getWriter();
                await writer.write(encoder.encode(msg));
                writer.releaseLock();
            }
            else {
                console.log('未打开串口');
                alertError('未打开串口', 5)
            }
        }

        function writeToBuffer(msg) {
            console.log('recive: ' + msg);
            var buffer = document.getElementById('buffer');
            buffer.innerHTML += msg.replaceAll('\r\n', '<br/>').replaceAll('\n', '<br/>');
            if (autoScroll) {
                buffer.scrollTop = buffer.scrollHeight;
            }
        }

        // ----------------------------------------
        // Utility
        // ----------------------------------------
        function alertMsg(msg, t, color) {
            t = t || 3;
            var alert = document.querySelector('#alert');
            alert.innerHTML = msg;
            alert.style.display = 'block';
            alert.style.left = (document.body.clientWidth - 300) / 2 + 'px';
            alert.style.top = '60px';
            alert.style.width = '300px';
            if (color) {
                alert.style.background = color;
            }

            setInterval(function() {
                alert.style.display = 'none';
            }, t * 1000);
        }

        function alertSuccess(msg, t) {
            alertMsg(msg, t, '#00cc00');
        }

        function alertError(msg, t) {
            alertMsg(msg, t, '#dd0000');
        }
    </script>
</body>
</html>
